package me.ingxin.android.keyboard

import android.annotation.SuppressLint
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.appcompat.app.AppCompatDialog
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import me.ingxin.android.keyboard.entry.Key
import me.ingxin.android.keyboard.view.IKeyboard
import me.ingxin.android.tools.dp2px

/**
 * Created by ingxin on 2023/2/6
 * 自定义键盘管理
 * @param editText 需要绑定的editText
 */
abstract class BaseKeyboard<T>(private val editText: EditText) where T : View, T : IKeyboard {

    private val handler = Handler(Looper.getMainLooper())
    private val onKeyListener = IKeyboard.OnKeyListener { onKeyConfirm(it) }
    private val keyIcons = HashMap<Int, Drawable?>()

    /**主题色*/
    var themeColor: Int? = null

    /**按键背景*/
    var keyBackground: Drawable? = null

    /**按键水平方向间距*/
    var horizontalGap: Float? = null

    /**按键竖直方向间距*/
    var verticalGap: Float? = null

    /**按键字体大小*/
    var keyTextSize: Float? = null

    /**按键字体颜色*/
    var keyTextColor: Int? = null

    /**键盘高度*/
    var height: Int

    /**键盘宽度*/
    var width: Int

    private var dialog: KeyboardDialog? = null

    init {
        val ctx = editText.context
        height = ctx.dp2px(260f)
        width = ViewGroup.LayoutParams.MATCH_PARENT
        bind(editText)
    }

    /**
     * 设置按键图标
     * @param keyCode 按键对应的code
     * @param drawable 按键图标
     */
    fun setKeyIcon(keyCode: Int, drawable: Drawable?) {
        keyIcons[keyCode] = drawable
    }

    /**
     * 当按键被确认触发后回调
     */
    protected open fun onKeyConfirm(key: Key) {
        val editable = editText.text
        val start = editText.selectionStart
        val end = editText.selectionEnd
        if (key.code == Key.CODE_DELETE) {
            // 回退键,删除字符
            if (editable != null && editable.isNotEmpty()) {
                if (start == end) {
                    if (start > 0) {
                        //光标开始和结束位置相同, 即没有选中内容
                        editable.delete(start - 1, start)
                    }
                } else {
                    //光标开始和结束位置不同, 即选中EditText中的内容
                    editable.delete(start, end)
                }
            }
        } else if (key.code >= 32) {
            //32-127为可见ascii码范围
            editable.replace(start, end, key.code.toChar().toString())
        } else if (key.code == Key.CODE_HIDE) {
            dismiss()
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    private fun bind(et: EditText) {
        et.isFocusable = true
        et.isFocusableInTouchMode = true
        //获取到焦点时禁止弹出键盘
        et.showSoftInputOnFocus = false
        et.setOnTouchListener { _, event ->
            if (event.action == MotionEvent.ACTION_UP && !isShowing()) {
                show()
            }
            false
        }

        et.setOnFocusChangeListener { _, hasFocus ->
            if (hasFocus) {
                show()
            } else {
                //切换场景，第一个dialog消失第二个还未显示则会有留白，延迟小段时间消失减少留白时间
                handler.postDelayed({ dismiss() }, 200)
            }
        }
    }

    /**
     * 显示安全键盘
     */
    fun show() {
        val showIme = isVisibleIME(editText)
        if (showIme) {
            hideIME()
        }
        if (isShowing()) {
            return
        }
        if (showIme) {
            //等待系统键盘收起后再显示，优化显示效果
            handler.postDelayed({ innerShow() }, 300)
        } else {
            innerShow()
        }
    }

    private fun innerShow() {
        //显示键盘容器dialog
        dialog?.dismiss()
        val keyboard = getKeyboardView()
        (keyboard.parent as? ViewGroup)?.removeAllViews()
        dialog = KeyboardDialog(keyboard, width, height)
        dialog?.show()

        keyboard.resetUI()
        themeColor?.let { keyboard.setThemeColor(it) }
        keyBackground?.let { keyboard.setKeyBackground(it) }
        horizontalGap?.let { keyboard.setHorizontalGap(it) }
        verticalGap?.let { keyboard.setVerticalGap(it) }
        keyTextSize?.let { keyboard.setKeyTextSize(it) }
        keyTextColor?.let { keyboard.setKeyTextColor(it) }
        for ((k, v) in keyIcons) {
            keyboard.setKeyIcon(k, v)
        }
        keyboard.setOnKeyListener(onKeyListener)
    }

    /**
     * 获取键盘view
     */
    protected abstract fun getKeyboardView(): T

    /**
     * 关闭键盘
     */
    fun dismiss() {
        dialog?.dismiss()
        dialog = null
    }

    /**
     * 是否正在显示
     */
    fun isShowing(): Boolean {
        return dialog?.isShowing == true
    }

    /**
     * 收起系统输入法
     */
    private fun hideIME() {
        val context = editText.context
        try {
            val imm = context.getSystemService(InputMethodManager::class.java)
            imm?.hideSoftInputFromWindow(
                editText.windowToken, InputMethodManager.HIDE_NOT_ALWAYS
            )
        } catch (ignore: Exception) {
            //do nothing
        }
    }

    private fun isVisibleIME(view: View): Boolean {
        val insets = ViewCompat.getRootWindowInsets(view)
        return insets?.isVisible(WindowInsetsCompat.Type.ime()) == true
    }

    /**
     * 键盘容器dialog
     */
    private class KeyboardDialog(
        private val keyboardView: View, private val width: Int, private val height: Int
    ) : AppCompatDialog(keyboardView.context) {

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(keyboardView)
            setCanceledOnTouchOutside(false)
            window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
            val lp = window?.attributes?.also {
                it.gravity = Gravity.BOTTOM
                it.height = height
                it.width = width
            }
            window?.attributes = lp
            //清除掉dimAmount，不会改变状态栏颜色,宽高必须为指定尺寸才会生效
            window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)

            //window可获得焦点情况下，仍把该window之外的任何event发送到该window之下的其他window
            window?.setFlags(
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
            )

            //点击事件发生在window之外，就会收到一个特殊的MotionEvent，为ACTION_OUTSIDE
            window?.setFlags(
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
            )
        }
    }

}